package rps.client.ui;

import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;

import javax.swing.JPanel;

import rps.client.UIController;
import rps.game.data.Figure;
import rps.game.data.FigureKind;

public class BoardPanel extends JPanel implements ActionListener, AnimatorPanel{
	private static final long serialVersionUID = 2722212933694798246L;
	public static final int ROWS = 6;
	public static final int COLS = 7;
	
	private GamePane gamePane;
	private Figure[] figures;
	private GameButton[] buttons = new GameButton[ROWS*COLS];
	private Figure switchHelp;
	
	private Image animatorImg;

	public BoardPanel(GamePane gamePane) {
		super();
		this.gamePane = gamePane;
		this.setMinimumSize(UIController.BOARD_DIMENSION);
		this.setMaximumSize(UIController.BOARD_DIMENSION);		
		initComponents();
	}
	@Override
	protected void paintComponent(Graphics g){
	    Image gamePaneBack = MediaProvider.getMediaProvider().getImage("gamePaneBack");
	    int size = gamePaneBack.getHeight(this)/2;
	    
		super.paintComponent(g); 
	    if (gamePaneBack != null){
	    	for(int i=0; i<this.getWidth(); i+=size){
	    		for(int j=0; j<this.getHeight(); j+=size){
	    			g.drawImage(gamePaneBack, i, j, size, size, this);
	    		}
	    	}
	    }
    }
	
	/**
	 * Sets the Layout of the boardPanel and adds the GameButtons with default parameters.
	 */
	private void initComponents(){
		setLayout(new GridLayout(ROWS, COLS));		
		for(int i=0; i<ROWS*COLS; i++){
				GameButton b = new GameButton(i);
				buttons[i] = b;
				b.addActionListener(this);			
				add(b);				
		}
		initialenable();
	}
	
	/**
	 * Enables only the 2 bottom rows, which are naturally owned by the playing player.
	 */
	public void initialenable(){
		for(int i=0; i<buttons.length; i++){
			if(i>27){
				buttons[i].setEnabled(true);
			}
			else{
				buttons[i].setEnabled(false);
			}
		}
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		GameButton eB = (GameButton) e.getSource();		
		actionPerformer(eB);
	}
	private void actionPerformer(GameButton eB) {		
		//If Flag not set place Flag
		if(gamePane.uiController.getPlacementPhase()==UIController.FLAGNOTSET){
			figures[eB.getIndex()] = new Figure(FigureKind.FLAG, gamePane.uiController.getPlayer());
			updateBoard(figures);
			gamePane.setPlacementPhaseWithEnabling(UIController.TRAPNOTSET);
		}
		//If Trap not set palce Trap and place the other moveable in a random assignment
		else if(gamePane.uiController.getPlacementPhase()==UIController.TRAPNOTSET){
			if(figures[eB.getIndex()]==null){
				figures[eB.getIndex()] = new Figure(FigureKind.TRAP, gamePane.uiController.getPlayer());
				updateBoard(figures);
				gamePane.generateRandomAssignment("moveable");
				gamePane.setPlacementPhaseWithEnabling(UIController.ALLSET);
			}
		}
		//If all figures are set wait for two of them to be selected and swap them
		else if(gamePane.uiController.getPlacementPhase()==UIController.ALLSET){
			if(!anySelected()){
				eB.setSelected(true);
			}			
			else if(eB.getSelected()){
				unselectAll();
			}
			else{
				eB.setSelected(true);
				ArrayList<Integer> selectedButtons = new ArrayList<Integer>();
				for(int i=0;i<buttons.length;i++){
					if(buttons[i].getSelected()) selectedButtons.add(i);
				}
				if(selectedButtons.size()==2){
					switchHelp = figures[selectedButtons.get(0)];
					figures[selectedButtons.get(0)] = figures[selectedButtons.get(1)];
					figures[selectedButtons.get(1)] = switchHelp;
					updateBoard(figures);
					unselectAll();
				}
				else{
					//should never happen
				}
			}
				
		}
		//Game already started and an event is either a move or requesting possible moves
		else{
			resetPossibleMove();
			resetAttackble();
			
			if(!anySelected()){
				eB.setSelected(true);
				enableSpecialButtons(gamePane.getPossibleMoves(eB.getIndex(), figures));
				setPossibleMove(gamePane.getPossibleMoves(eB.getIndex(), figures), eB.getIndex());
			}
			else if(eB.getSelected()){
				unselectAll();
				enableButtons(true);
			}
			else{
				ArrayList<Integer> selectedButtons = new ArrayList<Integer>();
				for(int i=0;i<buttons.length;i++){
					if(buttons[i].getSelected()) selectedButtons.add(i);
				}
				if(selectedButtons.size()==1){
					if(gamePane.getPossibleMoves(selectedButtons.get(0), figures).contains(eB.getIndex())){
						if(figures[selectedButtons.get(0)]!=null && figures[selectedButtons.get(0)].belongsTo(gamePane.uiController.getPlayer()))
							gamePane.gameController.move(selectedButtons.get(0), eB.getIndex());
						else
							gamePane.gameController.move(eB.getIndex(), selectedButtons.get(0));
						gamePane.setStatusText("Warte auf Gegner...");
						enableButtons(false);
						unselectAll();
					}
					else{
						unselectAll();
						enableButtons(true);
						actionPerformer(eB);
					}
				}
				else{
					//should never happen
				}
			}
		}
		repaintAll();
	}
	
	/**
	 * Updates the board with the given FigureArray and replaces the old one.
	 * If there is a null-Array given, the method creates a random Assignment.
	 * @param figures The new figure constellation.
	 */
	public void updateBoard(Figure[] figures){		
		if(figures == null) figures = new Figure[ROWS*COLS];
		this.figures = figures;
		for(int i=0; i<ROWS*COLS; i++){
			if(figures[i]!=null){
				buttons[i].setFigure(figures[i]);
				buttons[i].setPlayer(gamePane.uiController.getPlayer());
			}
			else{
				buttons[i].setFigure(null);
			}
		}		
	}
	
	/**
	 * Sets all buttons to unselected.
	 */
	public void unselectAll(){
		for(int i=0;i<buttons.length;i++){
			buttons[i].setSelected(false);
		}
	}
	
	/**
	 * Returns if any button in the Array is selected.
	 * @return
	 */
	public boolean anySelected(){
		for(int i=0;i<buttons.length;i++){
			if(buttons[i].getSelected()) return true;
		}
		return false;
	}
	
	/**
	 * Repaint all Buttons.
	 */
	public void repaintAll(){
		for(int i=0;i<buttons.length;i++){
			buttons[i].repaint();
		}
	}
	
	/**
	 * Enables all buttons, that belong the the given Player.
	 * @param enable True enables. False disables
	 * @param player
	 */
	public void enableButtons(boolean enable){
		for(int i=0; i<ROWS*COLS; i++){
			if(enable && figures[i]!=null && figures[i].belongsTo(gamePane.uiController.getPlayer())){
				buttons[i].setEnabled(true);
			}
			else{
				buttons[i].setEnabled(false);
			}
		}
	}
	
	/**
	 * Enables an amount of Buttons. The Array contains the index of the buttons to be enabled.
	 * @param list
	 */
	public void enableSpecialButtons(ArrayList<Integer> list){
		for(int i=0; i<list.size(); i++){
			buttons[list.get(i)].setEnabled(true);
		}
	}
	
	/**
	 * Sets the last move attribute and the direction that the button can be drawn correctly.
	 * @param fromIndex
	 * @param diff
	 */
	public void setLastMove(int fromIndex, int diff){
		if(diff>1) 			buttons[fromIndex].setLastMove(GameButton.UP);
		else if(diff==1) 	buttons[fromIndex].setLastMove(GameButton.LEFT);
		else if(diff==-1) 	buttons[fromIndex].setLastMove(GameButton.RIGHT);
		else 				buttons[fromIndex].setLastMove(GameButton.DOWN);
	}
	
	/**
	 * Resets the last move independent from the last move.
	 */
	public void resetLastMove(){
		for(int i=0;i<buttons.length; i++)
			buttons[i].setLastMove(-1);
	}
	
	/**
	 * Sets the possible move attribute and the direction that the button can be drawn correctly.
	 * If on a nearby field is an opponents figure mark it as attackable as well
	 * @param list
	 * @param fromIndex
	 */
	public void setPossibleMove(ArrayList<Integer> list, int fromIndex){
		for(int i=0; i<list.size(); i++){
			if(figures[list.get(i)]!=null && ! figures[list.get(i)].belongsTo(gamePane.uiController.getPlayer())){
				buttons[list.get(i)].setAttackble(true);
			}
			else{
				if(fromIndex - list.get(i) >1) 			buttons[list.get(i)].setPossibleMove(GameButton.UP);
				else if(fromIndex - list.get(i) ==1) 	buttons[list.get(i)].setPossibleMove(GameButton.LEFT);
				else if(fromIndex - list.get(i) ==-1) 	buttons[list.get(i)].setPossibleMove(GameButton.RIGHT);
				else 									buttons[list.get(i)].setPossibleMove(GameButton.DOWN);
			}
		}
	}
	
	/**
	 * Resets the attackable attribute of all buttons.
	 */
	public void resetAttackble(){
		for(int i=0;i<buttons.length; i++)
			buttons[i].setAttackble(false);
	}
	
	/**
	 * Resets the possible move attribute of all buttons.
	 */
	public void resetPossibleMove(){
		for(int i=0;i<buttons.length; i++)
			buttons[i].setPossibleMove(-1);
	}
	
	/**
	 * Returns an array of figures as they are on the current Board of the player
	 * @return
	 */
	public Figure[] getFigures(){
		return figures;
	}
	
	public void setAnimatorImage(Image animatorImg){
		this.animatorImg = animatorImg;
	}

	public void drawAnimatorImage(Point pt) {
		if(animatorImg != null){
			getGraphics().drawImage(animatorImg, pt.x, pt.y, UIController.BOARDPANEWIDTH/7, UIController.BOARDPANEHEIGHT/6, this);
		}
	}
	
	/**
	 * "Deletes" a button that the animator can be drawn correctly.
	 * Is afterwards re-implemented by the animator itself.
	 */
	public void hideButton(int index){
		buttons[index].setFigure(null);
		repaintAll();
	}
	
	public void reset() {
		updateBoard(null);
		resetAttackble();
		resetLastMove();
		resetPossibleMove();
		unselectAll();
		initialenable();
	}
}
